You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Studio disables providers' own update checks but previously kept using older installed CLIs across restarts. An old Codex installation can omit models available to the same account with a newer CLI. Claude Code had the same update gap.
Summary and scope
Check both installed providers against npm latest before starting sessions. Use #845's isolated installer, verify the executable, and atomically select it. Failed or offline updates retain the working CLI; newer or unrecognized local builds are preserved. Cancel updates when quitting during setup and bound doctor probes.
Progress uses the existing startup screen. Dev/smoke mode and SAPIOM_DISABLE_AGENT_UPDATES=1 skip update traffic. Provider configuration and history are preserved. Previous successful install directories remain available to running processes; automatic cache eviction is not implemented. Failed or cancelled attempts clean up their own unpublished prefix and temporary selector.
Related work
Merge after #845. This PR adds startup update policy and wiring to its managed-runtime foundation.
Validation
Combined release candidate: pnpm build / pnpm typecheck / pnpm lint — passed
Harness unit suite — 3,852 passed
Desktop suite after release fixes — 205 passed
Focused updater tests after failed-attempt cleanup — 18 passed
macOS full installer packaging and packaged smoke — 15 passed, 1 Windows-only skip
Focused generation/navigation/create-agent/composer browser suites — 55 passed
git diff --check — passed
The actual coordinator installed and selected Codex 0.153.4 using packaged Electron/Node and bundled npm on macOS, then reused it without reinstalling. Under the same authenticated account and empty caches, Codex 0.143.0 omitted newer account-visible models, while both npm 0.153.4 and the managed installation exposed them. Repeating the original version reproduced the omission. A separate packaged Linux test installed and reused 0.153.4 from 0.100.0.
The combined release candidate also completed real private Agent Map inference through managed Claude Code 2.1.263 and Codex 0.153.4, with source files unchanged. Model availability still depends on the account; these observations do not guarantee access to a model.
Final-head CI is attached to this PR. The unchanged agent-core permission test is incompatible with this cloud VM, and two unchanged filesystem-watcher expectations differ on macOS; Linux CI covers those tests. Interactive Windows testing remains unperformed.
Tests and documentation
Coverage includes both providers, newer/beta/unknown versions, offline reuse, failed/incomplete installs, cancellation, immutable selection, and Windows npm layout. Desktop documentation describes startup updates and the opt-out.
Compatibility and release impact
Breaking or externally visible changes: startup can include provider updates, with a 90-second installer deadline per provider plus bounded checks/cleanup. New and resumed processes use the verified selected version.
ynadge
changed the title
fix(harness-desktop): update coding agents before sessions start
fix(harness-desktop): update Claude and Codex on startup
Sep 6, 2026
Confidentiality: clean. The three changesets, the new packages/harness-desktop/README.md, and
every new comment name only Sapiom, Claude Code / Codex, npm and the public registry. No customer,
codename, business arrangement, private host, or incident reference. No hardcoded internal defaults
(the only URL is https://registry.npmjs.org/). Nothing in the diff discloses a vulnerability.
Findings
1. agent-versions/ grows without bound and is never reclaimed — MEDIUM-HIGH
packages/harness-desktop/src/main/agent-updates.ts only ever creates directories. Grepping the
package, agent-versions appears in exactly two places: boot.ts:346 (the root) and the README.
Nothing deletes anything, ever. Three leaks compound:
Superseded prefixes. Every accepted update mints <kind>/<version>-<uuid>/ and leaves the
previous one forever. agent-updates.test.ts:691 ("retains older prefixes when selecting a new
version") pins this as intended.
Failed prefixes.mkdir(prefix) happens before install; on failure, wrong version, or abort
the partially-populated tree stays. The retains the working executable…after %s test literally
writes partial-download into the prefix and never asserts it is cleaned up.
Orphaned selectors.active-<uuid>.json is written with flag: "wx", then options.signal?.throwIfAborted() runs before rename (agent-updates.ts, publish block). Abort
there — which is exactly what quitting during setup does — leaks the temp file permanently.
Failure scenario: a user on a machine where Codex moved 0.100.0 → 0.153.4 (the PR's own validation)
accumulates one full CLI install per accepted update. @anthropic-ai/claude-code and @openai/codex
are each tens to 100+ MB installed, so this is hundreds of MB to GBs silently parked in userData,
with no eviction path and no uninstall hook.
Fix is cheap and safe: GC at the top of ensureAgentUpdates — remove active-*.json temp files and
any <kind>/* directory matching INSTALL_DIRECTORY that is not the one named by active.json.
Doing it at boot is safe precisely because no PTY from the previous launch survives, so the
"processes still using them" argument in the README does not apply across restarts.
2. Every launch blocks on the network and a possible install, with no in-app opt-out — MEDIUM
ensureAgentUpdates is awaited in boot() before doctor and before the main window
(boot.ts:344), and iterates providers sequentially. Worst case per provider: 5s external probe +
5s managed probe + 5s registry + 90s install (installAgentVersion) + 5s verify probe ≈ 110s,
so ~3.5 min added to launch for a user whose two CLIs are both stale. The escape hatches are --dev, --smoke, and SAPIOM_DISABLE_AGENT_UPDATES=1 — none reachable by a user who double-clicks
the app, which is this package's entire target audience. There is no persisted setting and no
"skip" affordance on the setup window; the only user-side cancel is quitting.
Either move the update off the boot critical path (select on the next launch, which the immutable active.json design already supports), or add a Settings toggle alongside the app-updater
preferences so this is a user decision rather than an env var.
3. Agent package names are now a second source of truth — LOW-MEDIUM
managed-agent.ts hardcodes @anthropic-ai/claude-code and @openai/codex in AGENT_PACKAGES,
while agent-install.ts:88 exists specifically so the desktop installer does not hardcode them
("Parsing the constant instead of hardcoding the name keeps the desktop installer from drifting
from what the CLI itself tells users to run"), and boot.ts already imports both CLAUDE_INSTALL_COMMAND and CODEX_INSTALL_COMMAND. If the harness ever changes an install
command, the setup path follows it and the update path silently keeps installing the old package.
Derive AGENT_PACKAGES[*].package via packageSpecFromInstallCommand() from the two constants.
Notes (not findings)
The changeset levels are consistent with this repo's actual convention (minor reserved for
breaking-for-embedders while @sapiom/harness is pre-1.0; additive surface ships as patch), so patch for the new createCodexAdapter export and the binaryArgs/binaryEnv adapter options is
correct here. Both new option fields are load-bearing for the desktop host and carry JSDoc.
@sapiom/harness-desktop is private: true, so the new README and the agent-versions behavior
never reach an npm tarball. No files/exports/engines changes; no new runtime dependencies.
The verify-then-rename publish order, the INSTALL_DIRECTORY / startsWith(version-) selector
validation, the PID-reuse guard in windowsUpdateTree, and the windowsHide: true on every new child_process call are all correct and well covered.
Verdict: Request changes — the unbounded agent-versions/ growth (finding 1) should be fixed
before merge; findings 2 and 3 are product/maintenance calls the author can decide.
Delta reviewed: 06670449 → 4d594686 (3 commits: failed-attempt cleanup, process-group
ownership + ELECTRON_RUN_AS_NODE containment, CI timeout). Confidentiality: clean — the two
changeset edits, the new desktop-managed-installs.md, the README lines and the new comments name
only Sapiom/Studio, Claude Code, Codex, Electron and npm. No new runtime deps, no new public
surface, no hardcoded internal defaults.
Earlier findings the push did NOT fix
cleanup HTTP SDKs #1 (disk growth) — partially fixed, core still open. The finally block in agent-updates.ts:180 now removes this attempt's unpublishedPrefix/unpublishedSelector, which
closes the failed-install and orphaned-active-*.json leaks. Superseded prefixes are still
never reclaimed — agent-updates.test.ts:226 still pins retention and README.md now states
"automatic eviction is not implemented" — so the recurring, unbounded part of the finding stands:
one full CLI install (tens to 100+ MB) per accepted update, forever, in userData.
cleanup HTTP SDKs #1 caveat. Cleanup is best-effort (rm(...).catch(() => {})), and the quit-during-install case
is exactly when npm may still be writing into the prefix. With no boot-time GC, any cleanup that
loses that race leaves the partial tree permanently.
LOW — timeout no longer kills detached descendants.agent-update-process.ts:203 now skips process.kill(-pid) whenever exitedAt !== undefined. The PID-reuse motivation is sound, but a
descendant that closes its stdio (an npm lifecycle script that daemonizes) lets the supervisor exit
first, so the 90 s deadline and the quit path both terminate nothing — round-1 code killed the
group. Worth a comment stating the tradeoff, or reap by pgid identity rather than skipping.
The --import data:…delete process.env.ELECTRON_RUN_AS_NODE injection checks out: the flag is
supplied explicitly everywhere it is still needed (boot.ts:577, shim-files.ts:32), and managed-agent.test.ts plus the smoke.ts fixture verify non-leakage into the CLI and its children.
Nothing the earlier round asserted turned out to be wrong.
Verdict: Approve with comments — only finding #1's superseded-prefix growth still warrants a fix
before merge; the rest are the author's call.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Primary change type
Problem and motivation
Studio disables providers' own update checks but previously kept using older installed CLIs across restarts. An old Codex installation can omit models available to the same account with a newer CLI. Claude Code had the same update gap.
Summary and scope
Check both installed providers against npm
latestbefore starting sessions. Use #845's isolated installer, verify the executable, and atomically select it. Failed or offline updates retain the working CLI; newer or unrecognized local builds are preserved. Cancel updates when quitting during setup and bound doctor probes.Progress uses the existing startup screen. Dev/smoke mode and
SAPIOM_DISABLE_AGENT_UPDATES=1skip update traffic. Provider configuration and history are preserved. Previous successful install directories remain available to running processes; automatic cache eviction is not implemented. Failed or cancelled attempts clean up their own unpublished prefix and temporary selector.Related work
Merge after #845. This PR adds startup update policy and wiring to its managed-runtime foundation.
Validation
The actual coordinator installed and selected Codex 0.153.4 using packaged Electron/Node and bundled npm on macOS, then reused it without reinstalling. Under the same authenticated account and empty caches, Codex 0.143.0 omitted newer account-visible models, while both npm 0.153.4 and the managed installation exposed them. Repeating the original version reproduced the omission. A separate packaged Linux test installed and reused 0.153.4 from 0.100.0.
The combined release candidate also completed real private Agent Map inference through managed Claude Code 2.1.263 and Codex 0.153.4, with source files unchanged. Model availability still depends on the account; these observations do not guarantee access to a model.
Final-head CI is attached to this PR. The unchanged agent-core permission test is incompatible with this cloud VM, and two unchanged filesystem-watcher expectations differ on macOS; Linux CI covers those tests. Interactive Windows testing remains unperformed.
Tests and documentation
Coverage includes both providers, newer/beta/unknown versions, offline reuse, failed/incomplete installs, cancellation, immutable selection, and Windows npm layout. Desktop documentation describes startup updates and the opt-out.
Compatibility and release impact
Security
AI assistance
Checklist